package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.SkuHasStockTo;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.utils.BeanCopyUtils;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.UserLoginRespVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.OrderService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    ThreadPoolExecutor threadPool;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单确认页的数据
     *
     * @return 订单确认页
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        UserLoginRespVo loginRespVo = LoginUserInterceptor.loginUser.get();
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        /*
            异步Feign调用时,浏览器原请求所在主线程数据共享不到异步feign线程
            导致异步feign线程的拦截器拿不到requestAttr，需要set进其他线程
         */
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 1.远程获取会员收获地址
        CompletableFuture<Void> getAddressTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> memberAddressVos = memberFeignService.getAddress(loginRespVo.getId());
            confirmVo.setAddress(memberAddressVos);
        }, threadPool);
        // Feign的调用，创建请求模板，默认是未携带请求头

        // 2.远程获取选中购物项
        CompletableFuture<Void> getOrderItemTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> orderItemVos = cartFeignService.getCurrentUserCartItems();
            confirmVo.setItems(orderItemVos);
        }, threadPool).thenRunAsync(() -> {
            List<OrderItemVo> items = confirmVo.getItems();
            List<Long> ids = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R r = wareFeignService.getSkuHasStock(ids);
            if (r.getCode() == 0) {
                List<SkuHasStockTo> stockTos = r.getData(new TypeReference<List<SkuHasStockTo>>() {
                });
                if (!CollectionUtils.isEmpty(stockTos)) {
                    Map<Long, Boolean> map = stockTos.stream().collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getHasStock));
                    confirmVo.setStocks(map);
                }
            }
        });

        // 3.获取会员积分
        Integer integration = confirmVo.getIntegration();
        confirmVo.setIntegration(integration);

        // 前置异步任务获取数据，才能后续
        CompletableFuture.allOf(getAddressTask, getOrderItemTask).get();
//        // 4.获取订单总额
//        confirmVo.setTotal(confirmVo.getTotal());
//
//        // 5.获取应付价格
//        confirmVo.setPayPrice(confirmVo.getPayPrice());

        // 获取订单防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + loginRespVo.getId(), token, 30, TimeUnit.MINUTES);
        confirmVo.setOrderToken(token);

        return confirmVo;
    }

    /**
     * 提交订单数据
     *
     * @param vo 订单表单
     * @return 订单响应
     */
//    @GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        submitVoThreadLocal.set(vo);
        UserLoginRespVo loginRespVo = LoginUserInterceptor.loginUser.get();
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        responseVo.setCode(0);

        // 1.原子验证token
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String webToken = vo.getOrderToken();
        Long res = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(OrderConstant.USER_ORDER_TOKEN_PREFIX + loginRespVo.getId()), webToken);
        if (res == null || res == 0) {
            // 令牌验证失败
            responseVo.setCode(1);
            return responseVo;
        }

        // 2.创建订单
        OrderCreateTo order = createOrder();

        // 3.验价
        BigDecimal payAmount = order.getOrder().getPayAmount();
        BigDecimal payPrice = vo.getPayPrice();
        if (Math.abs(payAmount.subtract(payPrice).doubleValue()) > 0.01) {
            // 金额对比失败
            responseVo.setCode(2);
            return responseVo;
        }

        // 4.保存订单
        saveOrder(order);

        // 5.锁定库存
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(order.getOrder().getOrderSn());
        List<OrderItemVo> locks = order.getOrderItems().stream().map(item -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            orderItemVo.setSkuId(item.getSkuId());
            orderItemVo.setCount(item.getSkuQuantity());
            orderItemVo.setTitle(item.getSkuName());
            return orderItemVo;
        }).collect(Collectors.toList());
        wareSkuLockVo.setLocks(locks);
        R r = wareFeignService.orderLockStock(wareSkuLockVo);
        if (r.getCode() == 0) {
            // 锁定成功
            responseVo.setOrder(order.getOrder());
            // 模拟下单成功，锁定库存成功，其他业务异常
//            int i = 10 / 0;
            rabbitTemplate.convertAndSend(
                    "order-event-exchange",
                    "order.create.order",
                    order.getOrder());
            return responseVo;
        } else {
            // 锁定失败
            responseVo.setCode(3);
            return responseVo;
        }
    }

    /**
     * 根据订单号查询订单
     *
     * @param orderSn 订单号
     * @return 订单
     */
    @Override
    public OrderEntity queryStatusByOrderSn(String orderSn) {
        return baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }

    /**
     * 关单
     *
     * @param orderEntity 订单实体
     */
    @Override
    public void closeOrder(OrderEntity orderEntity) {
        // 查询订单最新状态
        OrderEntity byId = this.getById(orderEntity.getId());
        // 关单
        if (OrderStatusEnum.CREATE_NEW.getCode().equals(byId.getStatus())) {
            OrderEntity entity = new OrderEntity();
            entity.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(entity);
            // 发送给stock.release.stock.queue进行主动解锁库存
            OrderTo orderTo = BeanCopyUtils.copy(byId, OrderTo.class);
            rabbitTemplate.convertAndSend(
                    "order-event-exchange",
                    "order.release.other",
                    orderTo
            );
        }
    }

    /**
     * 关闭订单
     *
     * @param orderSn 订单号
     */
    @Override
    public void closeByOrderSn(String orderSn) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setStatus(OrderStatusEnum.CANCLED.getCode());
        baseMapper.update(orderEntity, new UpdateWrapper<OrderEntity>()
                .eq("order_sn", orderSn)
        );
    }

    /**
     * 保存订单数据
     *
     * @param order 订单创建to
     */
    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        List<OrderItemEntity> orderItemEntities = order.getOrderItems();

        this.save(orderEntity);

        // 注入订单id
        orderItemEntities = orderItemEntities.stream().peek(item -> item.setOrderId(orderEntity.getId())).collect(Collectors.toList());
        orderItemService.saveBatch(orderItemEntities);
    }

    /**
     * 创建订单
     *
     * @return 订单创建to
     */
    private OrderCreateTo createOrder() {
        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();
        OrderCreateTo orderCreateTo = new OrderCreateTo();

        // 1.生成订单实体
        OrderEntity orderEntity = buildOrderEntity(orderSubmitVo);
        orderCreateTo.setFare(orderEntity.getFreightAmount());
        orderCreateTo.setOrder(orderEntity);

        // 2.生成订单项实体
        List<OrderItemEntity> itemEntities = buildOrderItemEntity(orderEntity.getOrderSn());
        orderCreateTo.setOrderItems(itemEntities);

        // 3.计算价格积分
        computePrice(orderEntity, itemEntities);

        return orderCreateTo;
    }

    /**
     * 计算价格积分
     *
     * @param orderEntity  订单实体
     * @param itemEntities 订单项集合
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal totalCouponAmount = BigDecimal.ZERO;
        BigDecimal totalIntegrationAmount = BigDecimal.ZERO;
        BigDecimal totalPromotionAmount = BigDecimal.ZERO;
        Integer totalIntegration = 0;
        Integer totalGrowth = 0;
        for (OrderItemEntity itemEntity : itemEntities) {
            totalAmount = totalAmount.add(itemEntity.getRealAmount());
            totalCouponAmount = totalCouponAmount.add(itemEntity.getCouponAmount());
            totalIntegrationAmount = totalIntegrationAmount.add(itemEntity.getIntegrationAmount());
            totalPromotionAmount = totalPromotionAmount.add(itemEntity.getPromotionAmount());
            totalIntegration += itemEntity.getGiftIntegration();
            totalGrowth += itemEntity.getGiftGrowth();
        }

        // 订单总额
        orderEntity.setTotalAmount(totalAmount);
        // 应付总额
        orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount()));
        // 优惠券抵扣金额
        orderEntity.setCouponAmount(totalCouponAmount);
        // 积分抵扣金额
        orderEntity.setIntegrationAmount(totalIntegrationAmount);
        // 优惠后的总额
        orderEntity.setPromotionAmount(totalPromotionAmount);
        // 积分
        orderEntity.setIntegration(totalIntegration);
        // 成长值
        orderEntity.setGrowth(totalGrowth);

    }

    /**
     * 构建订单实体
     *
     * @param orderSubmitVo 订单提交数据
     * @return 订单实体
     */
    private OrderEntity buildOrderEntity(OrderSubmitVo orderSubmitVo) {
        OrderEntity orderEntity = new OrderEntity();
        UserLoginRespVo userLoginRespVo = LoginUserInterceptor.loginUser.get();

        // 1.1设置会员信息
        orderEntity.setMemberId(userLoginRespVo.getId());
        orderEntity.setMemberUsername(userLoginRespVo.getUsername());

        // 1.2 创建订单号
        String orderSn = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderSn);
        // 1.3 获取收货信息
        R r = wareFeignService.getFare(orderSubmitVo.getAddrId());
        if (r.getCode() == 0) {
            FareVo fareVo = r.getData(new TypeReference<FareVo>() {
            });
            orderEntity.setFreightAmount(fareVo.getFare());
            orderEntity.setReceiverCity(fareVo.getAddress().getCity());
            orderEntity.setReceiverDetailAddress(fareVo.getAddress().getDetailAddress());
            orderEntity.setReceiverName(fareVo.getAddress().getName());
            orderEntity.setReceiverPhone(fareVo.getAddress().getPhone());
            orderEntity.setReceiverPostCode(fareVo.getAddress().getPostCode());
            orderEntity.setReceiverProvince(fareVo.getAddress().getProvince());
            orderEntity.setReceiverRegion(fareVo.getAddress().getRegion());
        }
        // 1.4 订单的状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);
        orderEntity.setDeleteStatus(0);
        return orderEntity;
    }

    /**
     * 构建订单项实体
     *
     * @return 订单项实体
     */
    private List<OrderItemEntity> buildOrderItemEntity(String orderSn) {
        // 远程获取购物车最新购物项的最新数据
        List<OrderItemVo> cartItems = cartFeignService.getCurrentUserCartItems();
        if (!CollectionUtils.isEmpty(cartItems)) {
            return cartItems.stream().map(item -> {
                OrderItemEntity itemEntity = new OrderItemEntity();
                // 商品的spu信息
                R r = productFeignService.getSpuInfoBySkuId(item.getSkuId());
                if (r.getCode() == 0) {
                    SpuInfoVo spuInfoVo = r.getData(new TypeReference<SpuInfoVo>() {
                    });
                    itemEntity.setCategoryId(spuInfoVo.getCatalogId());
                    itemEntity.setSpuId(spuInfoVo.getId());
                    itemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
                    itemEntity.setSpuName(spuInfoVo.getSpuName());
                }
                // 商品的sku信息
                itemEntity.setSkuId(item.getSkuId());
                itemEntity.setSkuName(item.getTitle());
                itemEntity.setSkuPic(item.getImage());
                itemEntity.setSkuPrice(item.getPrice());
                itemEntity.setSkuQuantity(item.getCount());
                itemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(), ";"));
                itemEntity.setOrderSn(orderSn);
                // 商品的积分信息
                itemEntity.setGiftGrowth(item.getPrice().intValue());
                itemEntity.setGiftIntegration(item.getPrice().intValue());
                // 商品的价格
                itemEntity.setPromotionAmount(BigDecimal.ZERO);
                itemEntity.setCouponAmount(BigDecimal.ZERO);
                itemEntity.setIntegrationAmount(BigDecimal.ZERO);
                BigDecimal originAmount = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity()));
                BigDecimal realAmount = originAmount.subtract(itemEntity.getPromotionAmount())
                        .subtract(itemEntity.getCouponAmount())
                        .subtract(itemEntity.getIntegrationAmount());
                itemEntity.setRealAmount(realAmount);
                return itemEntity;
            }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

}